﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using HengTong.Model;
using HengTong.Model._Admin;
using HengTong.Model.Db;
using HengTong.Model.Db.Hr;
using HengTong.Model.Db.Hr.Bill;
using HengTong.Model.Db.System;
using HengTong.Model.Services;
using HengTong.ViewModel._Admin.SysUserJobPositionVms;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using TiaoTaoHelper.Extension.Object;
using TiaoTaoHelper.Models;
using TiaoTaoHelper.Wtm.Models;
using UUIDNext;
using WalkingTec.Mvvm.Core;

namespace HengTong.ViewModel.Hr.SysHrInterviewPoolVms;

public partial class SysHrInterviewPoolBatchVm
{
    /// <summary>
    /// 通过勾选ID生成员工档案入职数据、人员档案和系统账号（一站式录用）
    /// 功能：从录用候选池一键生成入职申请、人员档案、系统账号，实现候选人到员工的完整转换
    /// </summary>
    /// <returns>生成的数据数量和处理结果</returns>
    public async Task<Result<int>> GenOnboardingByIdsAsync()
    {
        var logger = Wtm.ServiceProvider.GetRequiredService<ILogger<SysHrInterviewPoolBatchVm>>();
        logger.LogInformation("开始执行一站式录用处理，操作用户：{User}", Wtm.LoginUserInfo?.ITCode);

        // 1. 基础参数验证
        if (Ids == null || !Ids.Any())
        {
            logger.LogWarning("录用处理失败：未选择候选人");
            return new Result<int>("请选择需要生成入职档案的候选人");
        }

        var ids = Ids.Select(Guid.Parse).ToList();
        logger.LogInformation("准备处理 {Count} 个候选人，ID列表：{Ids}", ids.Count, string.Join(", ", ids));

        try
        {
            // 2. 查询录用候选池及关联数据
            logger.LogDebug("开始查询录用候选池数据及关联的面试申请表数据");
            var query = await DC.Set<SysHrInterviewPool>()
                .Where(p => ids.Contains(p.ID))
                .Include(x => x.InterviewRequestBill)
                .ThenInclude(b => b.PersonEducations)
                .ThenInclude(e => e.Attachments)
                .Include(x => x.InterviewRequestBill)
                .ThenInclude(b => b.PersonFamilies)
                .Include(x => x.InterviewRequestBill)
                .ThenInclude(b => b.PersonWorks)
                .Include(x => x.InterviewRequestBill)
                .ThenInclude(b => b.PersonResumes)
                .Include(x => x.InterviewRequestBill)
                .ThenInclude(b => b.PersonProfessionalTitles)
                .AsNoTracking()
                .ToArrayAsync();

            if (!query.Any())
            {
                logger.LogWarning("未找到候选人数据，查询ID：{Ids}", string.Join(", ", ids));
                return new Result<int>("未找到有效的候选人");
            }

            // 3. 过滤有效数据
            var validCandidates = query.Where(p => p.InterviewRequestBill != null).ToArray();
            if (!validCandidates.Any())
            {
                logger.LogWarning("所有候选人都没有关联的面试申请数据，候选人数量：{Count}", query.Length);
                return new Result<int>("未找到有效的面试申请数据");
            }

            logger.LogInformation("找到 {ValidCount} 个有效候选人（共 {TotalCount} 个候选人）",
                validCandidates.Length, query.Length);

            // 4. 数据完整性验证
            logger.LogDebug("开始执行数据完整性验证");
            var validationError = await ValidateDataIntegrityAsync(validCandidates);
            if (validationError != null)
            {
                logger.LogWarning("数据完整性验证失败：{Error}", validationError.Message);
                return validationError;
            }

            logger.LogDebug("数据完整性验证通过");

            // 5. 开始事务处理
            var transaction = await DC.Database.BeginTransactionAsync();
            logger.LogDebug("开始数据库事务");

            try
            {
                // 6. 生成完整的录用数据
                var result = await GenerateCompleteOnboardingDataAsync(validCandidates);

                // 7. 检查生成结果
                if (string.IsNullOrEmpty(result.Message) || !result.Message.StartsWith("成功"))
                {
                    logger.LogError("录用数据生成失败：{Error}", result.Message);
                    await transaction.RollbackAsync();
                    return result;
                }

                // 8. 提交事务
                await DC.SaveChangesAsync();
                await transaction.CommitAsync();

                logger.LogInformation("一站式录用处理成功完成：{Message}", result.Message);
                return result;
            }
            catch (Exception ex)
            {
                logger.LogError(ex, "录用处理过程中发生异常");
                await transaction.RollbackAsync();
                return new Result<int>($"批量录用处理失败：{ex.Message}");
            }
            finally
            {
                await transaction.DisposeAsync();
                logger.LogDebug("数据库事务已释放");
            }
        }
        catch (Exception ex)
        {
            logger.LogError(ex, "一站式录用处理发生未预期的异常");
            return new Result<int>($"系统异常：{ex.Message}");
        }
    }

    /// <summary>
    /// 验证数据完整性
    /// 检查入职申请、人员档案、系统账号是否存在冲突，以及手机号格式是否正确
    /// </summary>
    /// <param name="validCandidates">有效的候选人数据</param>
    /// <returns>null表示验证通过，否则返回错误信息</returns>
    private async Task<Result<int>?> ValidateDataIntegrityAsync(
        SysHrInterviewPool[] validCandidates)
    {
        var logger = Wtm.ServiceProvider.GetRequiredService<ILogger<SysHrInterviewPoolBatchVm>>();

        // 提取关键数据用于验证
        var staffNos = validCandidates
            .Select(p => p.HiredStaffNo)
            .Where(s => !string.IsNullOrEmpty(s))
            .Distinct()
            .ToList();

        var candidateIds = validCandidates.Select(p => p.ID).ToList();
        var phones = validCandidates
            .Select(p => p.InterviewRequestBill.Phone)
            .Where(phone => !string.IsNullOrEmpty(phone))
            .Distinct()
            .ToList();

        logger.LogDebug("开始串行验证数据完整性，工号数量：{StaffCount}，手机号数量：{PhoneCount}",
            staffNos.Count, phones.Count);

        // 串行执行各种冲突检查，避免 DbContext 并发问题
        var result = await CheckOnboardingBillConflictsAsync(candidateIds, staffNos, validCandidates);
        if (result != null)
        {
            logger.LogWarning("入职申请单冲突验证发现问题：{Error}", result.Message);
            return result;
        }

        result = await CheckPersonConflictsAsync(staffNos, validCandidates);
        if (result != null)
        {
            logger.LogWarning("人员档案冲突验证发现问题：{Error}", result.Message);
            return result;
        }

        result = await CheckUserAccountConflictsAsync(staffNos, validCandidates);
        if (result != null)
        {
            logger.LogWarning("系统账号冲突验证发现问题：{Error}", result.Message);
            return result;
        }

        result = await CheckPhoneConflictsAsync(phones, validCandidates);
        if (result != null)
        {
            logger.LogWarning("手机号冲突验证发现问题：{Error}", result.Message);
            return result;
        }

        logger.LogDebug("所有数据完整性验证通过");
        return null;
    }

    /// <summary>
    /// 检查入职申请单冲突
    /// 验证是否已存在基于相同录用候选池ID的入职申请单
    /// </summary>
    private async Task<Result<int>?> CheckOnboardingBillConflictsAsync(
        List<Guid> candidateIds, List<string> staffNos, SysHrInterviewPool[] validCandidates)
    {
        var logger = Wtm.ServiceProvider.GetRequiredService<ILogger<SysHrInterviewPoolBatchVm>>();

        try
        {
            // 只检查是否已存在基于相同录用候选池ID的入职申请单
            var existingBills = await DC.Set<SysHrOnboardingRequestBill>()
                .AsNoTracking()
                .Where(b => b.InterviewPoolId.HasValue && candidateIds.Contains(b.InterviewPoolId.Value))
                .Select(b => new { b.InterviewPoolId, b.StaffNo, b.WorkflowStatus })
                .ToListAsync();

            if (existingBills.Any())
            {
                logger.LogWarning("发现 {Count} 个入职申请单冲突", existingBills.Count);

                // 找到冲突的候选人信息
                var conflictPoolIds = existingBills.Select(b => b.InterviewPoolId.Value).ToHashSet();
                var conflictCandidates = validCandidates
                    .Where(p => conflictPoolIds.Contains(p.ID))
                    .ToList();

                // 构建详细错误信息
                var errorMessages = conflictCandidates.Select((candidate, index) =>
                {
                    var bill = existingBills.First(b => b.InterviewPoolId == candidate.ID);
                    var candidateName = candidate.InterviewRequestBill?.Name ?? "未知";
                    var candidateStaffNo = candidate.HiredStaffNo ?? "无工号";
                    return $"{index + 1}. 候选人 {candidateName}({candidateStaffNo}) 已生成入职申请单";
                }).Take(10).ToList(); // 限制显示数量

                var message = conflictCandidates.Count > 10
                    ? $"以下候选人已存在入职申请单，无法重复生成：\n{string.Join("\n", errorMessages)}\n...等 {conflictCandidates.Count} 个候选人"
                    : $"以下候选人已存在入职申请单，无法重复生成：\n{string.Join("\n", errorMessages)}";

                return new Result<int>(message);
            }

            logger.LogDebug("入职申请单冲突检查通过");
            return null;
        }
        catch (Exception ex)
        {
            logger.LogError(ex, "检查入职申请单冲突时发生异常");
            return new Result<int>("检查入职申请单冲突时发生异常");
        }
    }

    /// <summary>
    /// 检查人员档案冲突
    /// 验证工号和手机号是否已在人员档案中存在
    /// </summary>
    private async Task<Result<int>?> CheckPersonConflictsAsync(
        List<string> staffNos, SysHrInterviewPool[] validCandidates)
    {
        var logger = Wtm.ServiceProvider.GetRequiredService<ILogger<SysHrInterviewPoolBatchVm>>();

        try
        {
            // 提取候选人的手机号
            var phones = validCandidates
                .Select(p => p.InterviewRequestBill?.Phone)
                .Where(phone => !string.IsNullOrWhiteSpace(phone))
                .Distinct()
                .ToList();

            // 检查工号和手机号冲突
            var existingPersons = await DC.Set<SysPerson>()
                .AsNoTracking()
                .Where(x => x.GetType() == typeof(SysPerson))
                .Where(p => staffNos.Contains(p.StaffNo) || phones.Contains(p.Phone))
                .Select(p => new { p.StaffNo, p.Phone, p.Name })
                .ToListAsync();

            if (existingPersons.Any())
            {
                logger.LogWarning("发现 {Count} 个人员档案冲突", existingPersons.Count);

                // 分别处理工号冲突和手机号冲突
                var conflictMessages = new List<string>();

                // 工号冲突
                var staffNoConflicts = existingPersons.Where(p => staffNos.Contains(p.StaffNo)).ToList();
                foreach (var conflict in staffNoConflicts)
                {
                    var candidate =
                        validCandidates.FirstOrDefault(c => c.InterviewRequestBill?.StaffNo == conflict.StaffNo);
                    if (candidate != null)
                    {
                        var candidateName = candidate.InterviewRequestBill?.Name ?? "未知";
                        conflictMessages.Add(
                            $"候选人 {candidateName}({conflict.StaffNo}) 的工号已存在于人员档案中（档案姓名：{conflict.Name}）");
                    }
                }

                // 手机号冲突
                var phoneConflicts = existingPersons.Where(p => phones.Contains(p.Phone)).ToList();
                foreach (var conflict in phoneConflicts)
                {
                    var candidate =
                        validCandidates.FirstOrDefault(c => c.InterviewRequestBill?.Phone == conflict.Phone);
                    if (candidate != null)
                    {
                        var candidateName = candidate.InterviewRequestBill?.Name ?? "未知";
                        conflictMessages.Add(
                            $"候选人 {candidateName} 的手机号 {conflict.Phone} 已存在于人员档案中（档案姓名：{conflict.Name}）");
                    }
                }

                // 限制显示数量
                var displayMessages = conflictMessages.Take(10).ToList();
                var message = conflictMessages.Count > 10
                    ? $"以下候选人信息与现有人员档案冲突，无法生成：\n{string.Join("\n", displayMessages.Select((msg, index) => $"{index + 1}. {msg}"))}\n...等 {conflictMessages.Count} 个冲突"
                    : $"以下候选人信息与现有人员档案冲突，无法生成：\n{string.Join("\n", displayMessages.Select((msg, index) => $"{index + 1}. {msg}"))}";

                return new Result<int>(message);
            }

            logger.LogDebug("人员档案冲突检查通过");
            return null;
        }
        catch (Exception ex)
        {
            logger.LogError(ex, "检查人员档案冲突时发生异常");
            return new Result<int>("检查人员档案冲突时发生异常");
        }
    }

    /// <summary>
    /// 检查系统账号冲突
    /// 验证工号是否已在系统用户表中存在
    /// </summary>
    private async Task<Result<int>?> CheckUserAccountConflictsAsync(
        List<string> staffNos, SysHrInterviewPool[] validCandidates)
    {
        var logger = Wtm.ServiceProvider.GetRequiredService<ILogger<SysHrInterviewPoolBatchVm>>();

        try
        {
            var existingUsers = await DC.Set<FrameworkUser>()
                .AsNoTracking()
                .Where(u => staffNos.Contains(u.ITCode))
                .Select(u => u.ITCode)
                .ToListAsync();

            if (existingUsers.Any())
            {
                logger.LogWarning("发现 {Count} 个系统账号冲突", existingUsers.Count);

                var conflictPersons = validCandidates
                    .Where(p => existingUsers.Contains(p.InterviewRequestBill.StaffNo))
                    .Select(p => $"{p.InterviewRequestBill.Name}({p.InterviewRequestBill.StaffNo})")
                    .Take(10)
                    .ToList();

                var suffix = existingUsers.Count > 10 ? $"等{existingUsers.Count}人" : "";
                return new Result<int>(
                    $"以下人员系统账号已存在：{string.Join("、", conflictPersons)}{suffix}");
            }

            logger.LogDebug("系统账号冲突检查通过");
            return null;
        }
        catch (Exception ex)
        {
            logger.LogError(ex, "检查系统账号冲突时发生异常");
            return new Result<int>("检查系统账号冲突时发生异常");
        }
    }

    /// <summary>
    /// 检查手机号冲突
    /// 验证手机号是否已被其他用户使用
    /// </summary>
    private async Task<Result<int>?> CheckPhoneConflictsAsync(
        List<string> phones, SysHrInterviewPool[] validCandidates)
    {
        var logger = Wtm.ServiceProvider.GetRequiredService<ILogger<SysHrInterviewPoolBatchVm>>();

        if (!phones.Any())
        {
            logger.LogDebug("无手机号数据，跳过手机号冲突检查");
            return null;
        }

        try
        {
            var existingPhoneUsers = await DC.Set<FrameworkUser>()
                .AsNoTracking()
                .Where(u => phones.Contains(u.CellPhone))
                .Select(u => u.CellPhone)
                .ToListAsync();

            if (existingPhoneUsers.Any())
            {
                logger.LogWarning("发现 {Count} 个手机号冲突", existingPhoneUsers.Count);

                var conflictPersons = validCandidates
                    .Where(p => existingPhoneUsers.Contains(p.InterviewRequestBill.Phone))
                    .Select(p => $"{p.InterviewRequestBill.Name}({p.InterviewRequestBill.Phone})")
                    .Take(10)
                    .ToList();

                var suffix = existingPhoneUsers.Count > 10 ? $"等{existingPhoneUsers.Count}个" : "";
                return new Result<int>(
                    $"以下手机号已关联其他账号：{string.Join("、", conflictPersons)}{suffix}");
            }

            logger.LogDebug("手机号冲突检查通过");
            return null;
        }
        catch (Exception ex)
        {
            logger.LogError(ex, "检查手机号冲突时发生异常");
            return new Result<int>("检查手机号冲突时发生异常");
        }
    }

    /// <summary>
    /// 生成完整的录用数据（入职申请、人员档案、系统账号）
    /// 这是核心业务逻辑，将候选人数据转换为正式员工的完整流程
    /// </summary>
    /// <param name="candidates">验证通过的候选人数据</param>
    /// <returns>生成结果和统计信息</returns>
    private async Task<Result<int>> GenerateCompleteOnboardingDataAsync(
        SysHrInterviewPool[] candidates)
    {
        var logger = Wtm.ServiceProvider.GetRequiredService<ILogger<SysHrInterviewPoolBatchVm>>();
        var currentTime = DateTime.Now;
        var currentUser = Wtm.LoginUserInfo?.ITCode;

        logger.LogInformation("开始生成录用数据，候选人数量：{Count}，操作用户：{User}",
            candidates.Length, currentUser);

        // 初始化批量数据集合
        var allEntities = new BatchEntityCollection();
        var users = new List<FrameworkUser>();
        var userPositions = new List<SysUserJobPosition>(); // 新增：岗位关联集合

        int processedCount = 0;
        foreach (var candidate in candidates)
        {
            var interviewBill = candidate.InterviewRequestBill;
            if (interviewBill == null)
            {
                logger.LogWarning("候选人 {CandidateId} 没有关联的面试申请表，跳过处理", candidate.ID);
                continue;
            }

            try
            {
                logger.LogDebug("开始处理候选人：{Name}({StaffNo})",
                    interviewBill.Name, interviewBill.StaffNo);

                // 1. 生成入职申请单
                var onboardingBill = CreateOnboardingBill(interviewBill, candidate.ID, currentTime, currentUser);

                // 生成入职申请编码
                using (var scope = Wtm.ServiceProvider.CreateScope())
                {
                    var codeService = scope.ServiceProvider.GetRequiredService<CodeGeneratorService>();
                    var onboardingCode = await codeService.GenerateCodeAsync(onboardingBill.GetType().Name);
                    if (onboardingCode == null)
                        throw new Exception($"生成入职申请编码失败，候选人：{interviewBill.Name}");

                    onboardingBill.BillCode = onboardingCode;
                }

                allEntities.OnboardingBills.Add(onboardingBill);
                logger.LogDebug("入职申请单创建成功，编码：{Code}", onboardingBill.BillCode);

                // 2. 生成人员档案
                var person = CreatePerson(interviewBill, currentTime, currentUser);
                allEntities.Persons.Add(person);
                logger.LogDebug("人员档案创建成功，ID：{PersonId}", person.ID);

                // 3. 生成系统账号
                var user = new FrameworkUser
                {
                    ID = Uuid.NewDatabaseFriendly(Database.SqlServer),
                    ITCode = person.StaffNo,
                    Name = person.Name,
                    CellPhone = person.Phone,
                    GroupId = person.GroupId,
                    Password = Utils.GetMD5String(Uuid.NewDatabaseFriendly(Database.SqlServer).ToString()),
                    PersonId = person.ID,
                    CreateBy = Wtm.LoginUserInfo?.ITCode,
                    UpdateBy = Wtm.LoginUserInfo?.ITCode,
                    CreateTime = DateTime.Now,
                    UpdateTime = DateTime.Now
                };
                users.Add(user);
                logger.LogDebug("系统账号创建成功，用户名：{ITCode}", user.ITCode);

                // 4. 创建用户岗位关联（必须创建，UpdateUserGroupAsync 方法依赖此关联）
                if (person.JobPositionId.HasValue)
                {
                    var userPosition = new SysUserJobPosition
                    {
                        ID = Uuid.NewDatabaseFriendly(Database.SqlServer),
                        UserId = user.ID,
                        JobPositionId = person.JobPositionId.Value,
                        IsMain = true
                    };
                    userPositions.Add(userPosition);
                    logger.LogDebug("用户 {ITCode} 岗位关联创建成功，岗位ID：{JobPositionId}",
                        user.ITCode, person.JobPositionId.Value);
                }
                else
                {
                    logger.LogWarning("用户 {ITCode} 没有岗位信息，将无法自动分配部门和角色", user.ITCode);
                }

                // 5. 处理子表数据（学历、家庭、工作经历等）
                ProcessChildTables(interviewBill, onboardingBill.ID, person.ID, currentTime, currentUser, allEntities);
                logger.LogDebug("子表数据处理完成");

                // 6. 更新录用候选池状态为已录用
                candidate.HrStatus = HrStatus.已录用;
                DC.UpdateProperty(candidate, x => x.HrStatus);
                candidate.UpdateTime = currentTime;
                DC.UpdateProperty(candidate, x => x.UpdateTime);
                candidate.UpdateBy = currentUser;
                DC.UpdateProperty(candidate, x => x.UpdateBy);

                logger.LogDebug("候选池状态已更新为已录用，候选人：{Name}({StaffNo})",
                    interviewBill.Name, interviewBill.StaffNo);

                processedCount++;

                // 每处理10个候选人记录一次进度
                if (processedCount % 10 == 0)
                    logger.LogInformation("已处理 {ProcessedCount} 个候选人", processedCount);
            }
            catch (Exception ex)
            {
                logger.LogError(ex, "处理候选人时发生异常：{Name}({StaffNo})",
                    interviewBill.Name, interviewBill.StaffNo);
                return new Result<int>($"处理候选人 {interviewBill.Name} 时发生异常：{ex.Message}");
            }
        }

        if (processedCount == 0)
        {
            logger.LogWarning("没有成功处理任何候选人");
            return new Result<int>("没有成功处理任何候选人");
        }

        // 批量保存到数据库
        logger.LogDebug("开始批量保存数据到数据库");
        try
        {
            await SaveBatchEntitiesAsync(allEntities, users, userPositions);
            logger.LogInformation("数据批量保存成功");

            // 批量更新用户的部门和角色关联
            await BatchUpdateUserRelationsAsync(users);
            logger.LogInformation("用户关联关系更新完成");
        }
        catch (Exception ex)
        {
            logger.LogError(ex, "批量保存数据或更新关联关系时发生异常");
            return new Result<int>($"数据处理失败：{ex.Message}");
        }

        // 返回处理结果
        var result = new Result<int>
        {
            Data = processedCount,
            Message =
                $"成功录用 {processedCount} 人：生成入职申请 {allEntities.OnboardingBills.Count} 个，人员档案 {allEntities.Persons.Count} 个，系统账号 {users.Count} 个"
        };

        logger.LogInformation("录用数据生成完成：{Message}", result.Message);
        return result;
    }

    /// <summary>
    /// 批量更新用户的部门和角色关联
    /// 利用现有的 UpdateUserGroupAsync 方法统一处理
    /// </summary>
    private async Task BatchUpdateUserRelationsAsync(List<FrameworkUser> users)
    {
        var logger = Wtm.ServiceProvider.GetRequiredService<ILogger<SysHrInterviewPoolBatchVm>>();

        if (!users.Any())
        {
            logger.LogDebug("没有用户需要更新关联关系");
            return;
        }

        logger.LogInformation("开始批量更新 {Count} 个用户的部门和角色关联", users.Count);

        var jobPositionVm = new SysUserJobPositionVm
        {
            DC = this.DC,
            Wtm = this.Wtm
        };

        var userCodes = new List<string>();
        int processedCount = 0;

        foreach (var user in users)
        {
            try
            {
                await jobPositionVm.UpdateUserGroupAsync(user.ID);
                userCodes.Add(user.ITCode);
                processedCount++;

                logger.LogDebug("用户 {ITCode} 的部门和角色关联更新完成", user.ITCode);

                // 每处理10个用户记录一次进度
                if (processedCount % 10 == 0)
                    logger.LogInformation("已更新 {ProcessedCount} 个用户的关联关系", processedCount);
            }
            catch (Exception ex)
            {
                logger.LogError(ex, "更新用户 {ITCode} 的部门和角色关联失败", user.ITCode);
                throw new Exception($"更新用户 {user.ITCode} 的关联关系失败: {ex.Message}");
            }
        }

        // 批量清除用户缓存
        if (userCodes.Any())
        {
            await Wtm.RemoveUserCache(userCodes.ToArray());
            logger.LogDebug("已清除 {Count} 个用户的缓存", userCodes.Count);
        }

        logger.LogInformation("批量更新用户关联关系完成，成功处理 {Count} 个用户", processedCount);
    }

    /// <summary>
    /// 批量保存实体数据到数据库
    /// 按照主表、子表的顺序进行批量插入，提高数据库操作效率
    /// </summary>
    private async Task SaveBatchEntitiesAsync(BatchEntityCollection entities, List<FrameworkUser> users,
        List<SysUserJobPosition> userPositions)
    {
        var logger = Wtm.ServiceProvider.GetRequiredService<ILogger<SysHrInterviewPoolBatchVm>>();

        try
        {
            // 先保存主表数据
            if (entities.OnboardingBills.Any())
            {
                await DC.Set<SysHrOnboardingRequestBill>().AddRangeAsync(entities.OnboardingBills);
                logger.LogDebug("批量添加入职申请单：{Count} 条", entities.OnboardingBills.Count);
            }

            if (entities.Persons.Any())
            {
                await DC.Set<SysPerson>().AddRangeAsync(entities.Persons);
                logger.LogDebug("批量添加人员档案：{Count} 条", entities.Persons.Count);
            }

            if (users.Any())
            {
                await DC.Set<FrameworkUser>().AddRangeAsync(users);
                logger.LogDebug("批量添加系统账号：{Count} 条", users.Count);
            }

            // 保存主表数据，以便获得生成的主键
            await DC.SaveChangesAsync();
            logger.LogDebug("主表数据保存完成");

            // 保存用户岗位关联（必须在用户保存之后）
            if (userPositions.Any())
            {
                await DC.Set<SysUserJobPosition>().AddRangeAsync(userPositions);
                logger.LogDebug("批量添加用户岗位关联：{Count} 条", userPositions.Count);
                await DC.SaveChangesAsync();
                logger.LogDebug("用户岗位关联保存完成");
            }

            // 串行保存子表数据
            if (entities.OnboardingEducations.Any())
            {
                await DC.Set<SysPersonEducation>().AddRangeAsync(entities.OnboardingEducations);
                logger.LogDebug("入职申请学历记录添加完成：{Count} 条", entities.OnboardingEducations.Count);
            }

            if (entities.PersonEducations.Any())
            {
                await DC.Set<SysPersonEducation>().AddRangeAsync(entities.PersonEducations);
                logger.LogDebug("人员档案学历记录添加完成：{Count} 条", entities.PersonEducations.Count);
            }

            if (entities.OnboardingFamilies.Any())
            {
                await DC.Set<SysPersonFamily>().AddRangeAsync(entities.OnboardingFamilies);
                logger.LogDebug("入职申请家庭成员记录添加完成：{Count} 条", entities.OnboardingFamilies.Count);
            }

            if (entities.PersonFamilies.Any())
            {
                await DC.Set<SysPersonFamily>().AddRangeAsync(entities.PersonFamilies);
                logger.LogDebug("人员档案家庭成员记录添加完成：{Count} 条", entities.PersonFamilies.Count);
            }

            if (entities.OnboardingWorks.Any())
            {
                await DC.Set<SysPersonWork>().AddRangeAsync(entities.OnboardingWorks);
                logger.LogDebug("入职申请工作经历记录添加完成：{Count} 条", entities.OnboardingWorks.Count);
            }

            if (entities.PersonWorks.Any())
            {
                await DC.Set<SysPersonWork>().AddRangeAsync(entities.PersonWorks);
                logger.LogDebug("人员档案工作经历记录添加完成：{Count} 条", entities.PersonWorks.Count);
            }

            if (entities.OnboardingResumes.Any())
            {
                await DC.Set<SysPersonResume>().AddRangeAsync(entities.OnboardingResumes);
                logger.LogDebug("入职申请履历记录添加完成：{Count} 条", entities.OnboardingResumes.Count);
            }

            if (entities.PersonResumes.Any())
            {
                await DC.Set<SysPersonResume>().AddRangeAsync(entities.PersonResumes);
                logger.LogDebug("人员档案履历记录添加完成：{Count} 条", entities.PersonResumes.Count);
            }

            if (entities.OnboardingProfessionalTitles.Any())
            {
                await DC.Set<SysPersonProfessionalTitle>().AddRangeAsync(entities.OnboardingProfessionalTitles);
                logger.LogDebug("入职申请荣誉职称记录添加完成：{Count} 条", entities.OnboardingProfessionalTitles.Count);
            }

            if (entities.PersonProfessionalTitles.Any())
            {
                await DC.Set<SysPersonProfessionalTitle>().AddRangeAsync(entities.PersonProfessionalTitles);
                logger.LogDebug("人员档案荣誉职称记录添加完成：{Count} 条", entities.PersonProfessionalTitles.Count);
            }

            // 保存所有子表数据
            await DC.SaveChangesAsync();
            logger.LogDebug("子表数据保存完成");

            logger.LogInformation("所有实体数据批量添加完成");
        }
        catch (Exception ex)
        {
            logger.LogError(ex, "批量保存实体数据时发生异常");
            throw;
        }
    }


    /// <summary>
    /// 创建用户关联关系（部门、岗位、角色）
    /// 根据人员的实际岗位直接分配角色和权限
    /// </summary>
    private void CreateUserRelations(FrameworkUser user, SysPerson person,
        List<SysJobPosition> positions, DateTime currentTime, UserRelationCollection relations)
    {
        var logger = Wtm.ServiceProvider.GetRequiredService<ILogger<SysHrInterviewPoolBatchVm>>();

        // 根据人员的岗位ID查找具体岗位（人员已经有明确的岗位）
        var position = positions.FirstOrDefault(e => e.ID == person.JobPositionId);

        if (position == null)
        {
            // 如果岗位不存在，赋予默认角色
            logger.LogWarning("用户 {ITCode} 的岗位ID {JobPositionId} 不存在，赋予默认角色",
                user.ITCode, person.JobPositionId);

            var defaultUserRole = new FrameworkUserRole
            {
                ID = Uuid.NewDatabaseFriendly(Database.SqlServer),
                UserCode = user.ITCode,
                RoleCode = "102", // 默认角色代码
                CreateBy = user.ITCode,
                CreateTime = currentTime,
                UpdateBy = user.ITCode,
                UpdateTime = currentTime
            };
            relations.UserRoles.Add(defaultUserRole);
            return;
        }

        logger.LogDebug("用户 {ITCode} 匹配到岗位：{PositionName}(ID:{PositionId})",
            user.ITCode, position.Name, position.ID);

        // 添加用户岗位关联
        var userPosition = new SysUserJobPosition
        {
            ID = Uuid.NewDatabaseFriendly(Database.SqlServer),
            UserId = user.ID,
            JobPositionId = position.ID,
            IsMain = true
        };
        relations.UserPositions.Add(userPosition);

        // 添加岗位相关的角色
        if (position.Roles?.Any() == true)
        {
            foreach (var positionRole in position.Roles)
            {
                var userRole = new FrameworkUserRole
                {
                    ID = Uuid.NewDatabaseFriendly(Database.SqlServer),
                    RoleCode = positionRole.Role.RoleCode,
                    TenantCode = positionRole.Role.TenantCode,
                    UserCode = user.ITCode,
                    CreateBy = user.ITCode,
                    CreateTime = currentTime,
                    UpdateBy = user.ITCode,
                    UpdateTime = currentTime
                };
                relations.UserRoles.Add(userRole);

                logger.LogDebug("为用户 {ITCode} 添加角色：{RoleCode}", user.ITCode, positionRole.Role.RoleCode);
            }
        }
        else
        {
            logger.LogWarning("岗位 {PositionName}(ID:{PositionId}) 没有配置角色，为用户 {ITCode} 赋予默认角色",
                position.Name, position.ID, user.ITCode);

            // 岗位没有配置角色时，赋予默认角色
            var defaultUserRole = new FrameworkUserRole
            {
                ID = Uuid.NewDatabaseFriendly(Database.SqlServer),
                UserCode = user.ITCode,
                RoleCode = "109", // 默认实习角色代码
                CreateBy = user.ITCode,
                CreateTime = currentTime,
                UpdateBy = user.ITCode,
                UpdateTime = currentTime
            };
            relations.UserRoles.Add(defaultUserRole);
        }

        // 添加部门关联
        if (position.Group != null)
        {
            var userGroup = new FrameworkUserGroup
            {
                UserCode = user.ITCode,
                GroupCode = position.Group.GroupCode,
                TenantCode = position.Group.TenantCode,
                CreateBy = user.ITCode,
                CreateTime = currentTime,
                UpdateBy = user.ITCode,
                UpdateTime = currentTime
            };
            relations.UserGroups.Add(userGroup);

            logger.LogDebug("为用户 {ITCode} 添加部门：{GroupCode}", user.ITCode, position.Group.GroupCode);
        }
        else
        {
            logger.LogWarning("岗位 {PositionName}(ID:{PositionId}) 没有关联部门信息", position.Name, position.ID);
        }
    }

    /// <summary>
    /// 用户关系集合类
    /// 用于管理用户的部门、岗位、角色关联数据
    /// </summary>
    private class UserRelationCollection
    {
        /// <summary>用户角色关联集合</summary>
        public List<FrameworkUserRole> UserRoles { get; } = new();

        /// <summary>用户岗位关联集合</summary>
        public List<SysUserJobPosition> UserPositions { get; } = new();

        /// <summary>用户部门关联集合</summary>
        public List<FrameworkUserGroup> UserGroups { get; } = new();
    }

    private async Task<string> GetHiredStaffNoAsync(Guid interviewRequestBillId)
    {
        return await DC.Set<SysHrInterviewPool>()
            .AsNoTracking()
            .Where(x => x.InterviewRequestBillId == interviewRequestBillId)
            .Select(x => x.HiredStaffNo)
            .FirstOrDefaultAsync();
    }

    /// <summary>
    /// 创建入职申请单
    /// 从面试申请表数据复制创建入职申请单，设置必要的状态和关联关系
    /// </summary>
    private SysHrOnboardingRequestBill CreateOnboardingBill(SysHrInterviewRequestBill interviewBill,
        Guid candidateId, DateTime currentTime, string? currentUser)
    {
        var onboardingBill = new SysHrOnboardingRequestBill();

        // 复制匹配的属性
        interviewBill.CloneMatchingProperties(onboardingBill);
        // 覆盖为候选池中的新工号（若存在）
        var poolNo = GetHiredStaffNoAsync(interviewBill.ID).Result;
        onboardingBill.StaffNo = poolNo;

        var staffNo = interviewBill.StaffNo;

        // 强制清空所有可能的导航属性
        onboardingBill.PersonEducations = null;
        onboardingBill.PersonFamilies = null;
        onboardingBill.PersonWorks = null;
        onboardingBill.PersonResumes = null;
        onboardingBill.PersonProfessionalTitles = null;

        // 设置入职申请单特有的属性
        onboardingBill.ID = Uuid.NewDatabaseFriendly(Database.SqlServer);
        onboardingBill.InterviewPoolId = candidateId; // 关联录用候选池ID
        onboardingBill.State = PersonState.试用期; // 设置为试用期状态
        onboardingBill.HireDate = DateTime.Now; // 默认入职日期为当前日期
        onboardingBill.EmploymentType = EmploymentType.全职; // 默认全职
        onboardingBill.ContractType = ContractType.固定期限; // 默认固定期限
        onboardingBill.BillStatus = BillStatus.草稿;
        onboardingBill.WorkflowStatus = WorkflowStatus.待提交;
        onboardingBill.CreateTime = currentTime;
        onboardingBill.UpdateTime = currentTime;
        onboardingBill.CreateBy = staffNo;
        onboardingBill.UpdateBy = currentUser;

        return onboardingBill;
    }

    /// <summary>
    /// 创建人员档案
    /// 从面试申请表数据复制创建正式的人员档案，设置为在职状态
    /// </summary>
    private SysPerson CreatePerson(SysHrInterviewRequestBill interviewBill,
        DateTime currentTime, string? currentUser)
    {
        var person = new SysPerson();

        // 复制匹配的属性
        interviewBill.CloneMatchingProperties(person);

        // 强制清空所有可能的导航属性
        person.PersonEducations = null;
        person.PersonFamilies = null;
        person.PersonWorks = null;
        person.PersonResumes = null;
        person.PersonProfessionalTitles = null;

        // 设置人员档案特有的属性
        person.ID = Uuid.NewDatabaseFriendly(Database.SqlServer);
        person.State = PersonState.待入职; // 设置为试用期状态
        person.CreateTime = currentTime;
        person.UpdateTime = currentTime;
        person.UpdateBy = currentUser;
        var poolNo = GetHiredStaffNoAsync(interviewBill.ID).Result;
        person.StaffNo = poolNo;
        person.CreateBy = poolNo;

        return person;
    }

    /// <summary>
    /// 处理所有子表数据
    /// 包括学历、家庭成员、工作经历、个人履历、荣誉职称等信息
    /// </summary>
    private void ProcessChildTables(SysHrInterviewRequestBill interviewBill,
        Guid onboardingBillId, Guid personId, DateTime currentTime, string? currentUser,
        BatchEntityCollection entities)
    {
        var logger = Wtm.ServiceProvider.GetRequiredService<ILogger<SysHrInterviewPoolBatchVm>>();

        try
        {
            // 处理各种子表数据，每种数据都需要为入职申请和人员档案创建两份
            ProcessEducations(interviewBill.PersonEducations, onboardingBillId, personId, currentTime, currentUser,
                entities);
            ProcessFamilies(interviewBill.PersonFamilies, onboardingBillId, personId, currentTime, currentUser,
                entities);
            ProcessWorks(interviewBill.PersonWorks, onboardingBillId, personId, currentTime, currentUser, entities);
            ProcessResumes(interviewBill.PersonResumes, onboardingBillId, personId, currentTime, currentUser, entities);
            ProcessProfessionalTitles(interviewBill.PersonProfessionalTitles, onboardingBillId, personId, currentTime,
                currentUser, entities);

            logger.LogDebug("子表数据处理完成，入职申请ID：{OnboardingId}，人员档案ID：{PersonId}",
                onboardingBillId, personId);
        }
        catch (Exception ex)
        {
            logger.LogError(ex, "处理子表数据时发生异常");
            throw;
        }
    }

    /// <summary>
    /// 处理学历信息
    /// 为入职申请和人员档案分别创建学历记录，包括附件信息
    /// </summary>
    private void ProcessEducations(ICollection<SysPersonEducation> educations,
        Guid onboardingBillId, Guid personId, DateTime currentTime, string? currentUser,
        BatchEntityCollection entities)
    {
        if (educations?.Any() != true) return;

        var logger = Wtm.ServiceProvider.GetRequiredService<ILogger<SysHrInterviewPoolBatchVm>>();

        foreach (var item in educations)
        {
            // 为入职申请创建学历记录
            var onboardingEducation = CloneEducation(item, onboardingBillId, currentTime, currentUser);
            entities.OnboardingEducations.Add(onboardingEducation);

            // 为人员档案创建学历记录
            var personEducation = CloneEducation(item, personId, currentTime, currentUser);
            entities.PersonEducations.Add(personEducation);
        }

        logger.LogDebug("处理了 {Count} 条学历记录", educations.Count);
    }

    /// <summary>
    /// 克隆学历信息
    /// 创建新的学历记录，包括附件信息的复制
    /// </summary>
    private SysPersonEducation CloneEducation(SysPersonEducation source,
        Guid parentId, DateTime currentTime, string? currentUser)
    {
        var education = new SysPersonEducation();
        source.CloneMatchingProperties(education);

        // 设置新的ID和关联关系
        education.ID = Uuid.NewDatabaseFriendly(Database.SqlServer);
        education.PersonId = parentId;
        education.Person = null; // 清空导航属性，避免EF追踪问题
        education.CreateTime = currentTime;
        education.UpdateTime = currentTime;
        education.CreateBy = currentUser;
        education.UpdateBy = currentUser;

        // 复制附件信息
        if (source.Attachments?.Any() == true)
        {
            education.Attachments = source.Attachments.Select(att =>
            {
                var newAttachment = new SysPersonEducationFile();
                att.CloneMatchingProperties(newAttachment);
                newAttachment.ID = Uuid.NewDatabaseFriendly(Database.SqlServer);
                newAttachment.PersonEducationId = education.ID;
                return newAttachment;
            }).ToList();
        }

        return education;
    }

    /// <summary>
    /// 处理家庭成员信息
    /// 为入职申请和人员档案分别创建家庭成员记录
    /// </summary>
    private void ProcessFamilies(ICollection<SysPersonFamily> families,
        Guid onboardingBillId, Guid personId, DateTime currentTime, string? currentUser,
        BatchEntityCollection entities)
    {
        if (families?.Any() != true) return;

        var logger = Wtm.ServiceProvider.GetRequiredService<ILogger<SysHrInterviewPoolBatchVm>>();

        foreach (var item in families)
        {
            var onboardingFamily = CloneFamily(item, onboardingBillId, currentTime, currentUser);
            var personFamily = CloneFamily(item, personId, currentTime, currentUser);

            entities.OnboardingFamilies.Add(onboardingFamily);
            entities.PersonFamilies.Add(personFamily);

            // 输出ID调试信息
            logger.LogDebug("家庭成员记录ID - 原始ID: {SourceId}, 入职申请ID: {OnboardingId}, 人员档案ID: {PersonId}",
                item.ID, onboardingFamily.ID, personFamily.ID);
        }

        logger.LogDebug("处理了 {Count} 条家庭成员记录", families.Count);

        // 输出所有OnboardingFamilies的ID
        logger.LogDebug("入职申请家庭成员记录ID列表: {Ids}",
            string.Join(", ", entities.OnboardingFamilies.Select(f => f.ID)));
        logger.LogDebug("人员档案家庭成员记录ID列表: {Ids}",
            string.Join(", ", entities.PersonFamilies.Select(f => f.ID)));
    }

    /// <summary>
    /// 克隆家庭成员信息
    /// </summary>
    private SysPersonFamily CloneFamily(SysPersonFamily source,
        Guid parentId, DateTime currentTime, string? currentUser)
    {
        var family = new SysPersonFamily();
        source.CloneMatchingProperties(family);
        family.Person = null;
        family.ID = Uuid.NewDatabaseFriendly(Database.SqlServer);
        family.PersonId = parentId;
        family.CreateTime = currentTime;
        family.UpdateTime = currentTime;
        family.CreateBy = currentUser;
        family.UpdateBy = currentUser;
        return family;
    }

    /// <summary>
    /// 处理工作经历信息
    /// 为入职申请和人员档案分别创建工作经历记录
    /// </summary>
    private void ProcessWorks(ICollection<SysPersonWork> works,
        Guid onboardingBillId, Guid personId, DateTime currentTime, string? currentUser,
        BatchEntityCollection entities)
    {
        if (works?.Any() != true) return;

        var logger = Wtm.ServiceProvider.GetRequiredService<ILogger<SysHrInterviewPoolBatchVm>>();

        foreach (var item in works)
        {
            entities.OnboardingWorks.Add(CloneWork(item, onboardingBillId, currentTime, currentUser));
            entities.PersonWorks.Add(CloneWork(item, personId, currentTime, currentUser));
        }

        logger.LogDebug("处理了 {Count} 条工作经历记录", works.Count);
    }

    /// <summary>
    /// 克隆工作经历信息
    /// </summary>
    private SysPersonWork CloneWork(SysPersonWork source,
        Guid parentId, DateTime currentTime, string? currentUser)
    {
        var work = new SysPersonWork();
        source.CloneMatchingProperties(work);
        work.Person = null;
        work.ID = Uuid.NewDatabaseFriendly(Database.SqlServer);
        work.PersonId = parentId;
        work.CreateTime = currentTime;
        work.UpdateTime = currentTime;
        work.CreateBy = currentUser;
        work.UpdateBy = currentUser;
        return work;
    }

    /// <summary>
    /// 处理个人履历信息
    /// 为入职申请和人员档案分别创建履历记录
    /// </summary>
    private void ProcessResumes(ICollection<SysPersonResume> resumes,
        Guid onboardingBillId, Guid personId, DateTime currentTime, string? currentUser,
        BatchEntityCollection entities)
    {
        if (resumes?.Any() != true) return;

        var logger = Wtm.ServiceProvider.GetRequiredService<ILogger<SysHrInterviewPoolBatchVm>>();

        foreach (var item in resumes)
        {
            entities.OnboardingResumes.Add(CloneResume(item, onboardingBillId, currentTime, currentUser));
            entities.PersonResumes.Add(CloneResume(item, personId, currentTime, currentUser));
        }

        logger.LogDebug("处理了 {Count} 条个人履历记录", resumes.Count);
    }

    /// <summary>
    /// 克隆个人履历信息
    /// </summary>
    private SysPersonResume CloneResume(SysPersonResume source,
        Guid parentId, DateTime currentTime, string? currentUser)
    {
        var resume = new SysPersonResume();
        source.CloneMatchingProperties(resume);
        resume.Person = null;
        resume.OriginalGroup = null;
        resume.OriginalPosition = null;
        resume.NewGroup = null;
        resume.NewPosition = null;
        resume.ID = Uuid.NewDatabaseFriendly(Database.SqlServer);
        resume.PersonId = parentId;
        resume.CreateTime = currentTime;
        resume.UpdateTime = currentTime;
        resume.CreateBy = currentUser;
        resume.UpdateBy = currentUser;
        return resume;
    }

    /// <summary>
    /// 处理荣誉职称信息
    /// 为入职申请和人员档案分别创建荣誉职称记录
    /// </summary>
    private void ProcessProfessionalTitles(ICollection<SysPersonProfessionalTitle> titles,
        Guid onboardingBillId, Guid personId, DateTime currentTime, string? currentUser,
        BatchEntityCollection entities)
    {
        if (titles?.Any() != true) return;

        var logger = Wtm.ServiceProvider.GetRequiredService<ILogger<SysHrInterviewPoolBatchVm>>();

        foreach (var item in titles)
        {
            entities.OnboardingProfessionalTitles.Add(CloneProfessionalTitle(item, onboardingBillId, currentTime,
                currentUser));
            entities.PersonProfessionalTitles.Add(CloneProfessionalTitle(item, personId, currentTime, currentUser));
        }

        logger.LogDebug("处理了 {Count} 条荣誉职称记录", titles.Count);
    }

    /// <summary>
    /// 克隆荣誉职称信息
    /// </summary>
    private SysPersonProfessionalTitle CloneProfessionalTitle(SysPersonProfessionalTitle source,
        Guid parentId, DateTime currentTime, string? currentUser)
    {
        var title = new SysPersonProfessionalTitle();
        source.CloneMatchingProperties(title);
        title.Person = null;
        title.ProfessionalTitleType = null;
        title.CertificateDocument = null;
        title.ID = Uuid.NewDatabaseFriendly(Database.SqlServer);
        title.PersonId = parentId;
        title.CreateTime = currentTime;
        title.UpdateTime = currentTime;
        title.CreateBy = currentUser;
        title.UpdateBy = currentUser;
        return title;
    }


    /// <summary>
    /// 批量实体集合类
    /// 用于管理所有需要批量插入的实体数据，提高代码组织性和可维护性
    /// </summary>
    private class BatchEntityCollection
    {
        /// <summary>入职申请单集合</summary>
        public List<SysHrOnboardingRequestBill> OnboardingBills { get; } = new();

        /// <summary>人员档案集合</summary>
        public List<SysPerson> Persons { get; } = new();

        /// <summary>入职申请的学历记录集合</summary>
        public List<SysPersonEducation> OnboardingEducations { get; } = new();

        /// <summary>人员档案的学历记录集合</summary>
        public List<SysPersonEducation> PersonEducations { get; } = new();

        /// <summary>入职申请的家庭成员记录集合</summary>
        public List<SysPersonFamily> OnboardingFamilies { get; } = new();

        /// <summary>人员档案的家庭成员记录集合</summary>
        public List<SysPersonFamily> PersonFamilies { get; } = new();

        /// <summary>入职申请的工作经历记录集合</summary>
        public List<SysPersonWork> OnboardingWorks { get; } = new();

        /// <summary>人员档案的工作经历记录集合</summary>
        public List<SysPersonWork> PersonWorks { get; } = new();

        /// <summary>入职申请的个人履历记录集合</summary>
        public List<SysPersonResume> OnboardingResumes { get; } = new();

        /// <summary>人员档案的个人履历记录集合</summary>
        public List<SysPersonResume> PersonResumes { get; } = new();

        /// <summary>入职申请的荣誉职称记录集合</summary>
        public List<SysPersonProfessionalTitle> OnboardingProfessionalTitles { get; } = new();

        /// <summary>人员档案的荣誉职称记录集合</summary>
        public List<SysPersonProfessionalTitle> PersonProfessionalTitles { get; } = new();
    }


    // csharp
    public async Task<Result<int>> BatchUpdateHiredStaffNoAsync(List<UpdateHiredStaffNoInput> items)
    {
        if (items == null || items.Count == 0)
            return new Result<int>("参数不能为空");

        var normalized = items
            .Where(x => !string.IsNullOrWhiteSpace(x.Id) && !string.IsNullOrWhiteSpace(x.NewHiredStaffNo))
            .Select(x => new { Id = x.Id.Trim(), NewNo = x.NewHiredStaffNo.Trim() })
            .ToList();

        if (normalized.Count == 0)
            return new Result<int>("无有效的待更新数据");

        // 0) 工号格式校验：只能为数字
        var invalidNos = normalized
            .Where(x => !System.Text.RegularExpressions.Regex.IsMatch(x.NewNo, "^[0-9]+$"))
            .Select(x => x.NewNo)
            .Distinct()
            .ToList();
        if (invalidNos.Count > 0)
            return new Result<int>($"以下工号格式非法（仅允许数字）：{string.Join(", ", invalidNos)}");

        // 1) 入参内重复校验：Id 不重复、工号不重复
        var dupIds = normalized.GroupBy(x => x.Id).Where(g => g.Count() > 1).Select(g => g.Key).ToList();
        if (dupIds.Count > 0)
            return new Result<int>($"参数中存在重复的ID：{string.Join(", ", dupIds)}");

        var dupNosInArgs = normalized.GroupBy(x => x.NewNo).Where(g => g.Count() > 1).Select(g => g.Key).ToList();
        if (dupNosInArgs.Count > 0)
            return new Result<int>($"参数中存在重复的工号：{string.Join(", ", dupNosInArgs)}");

        // 2) 人员档案(sysperson) 冲突校验
        var newNos = normalized.Select(x => x.NewNo).Distinct().ToList();
        var existNosInPerson = await DC.Set<SysPerson>()
            .AsNoTracking()
            .Where(p => newNos.Contains(p.StaffNo))
            .Select(p => p.StaffNo)
            .ToListAsync();
        if (existNosInPerson.Count > 0)
            return new Result<int>($"以下工号已存在于人员档案，不允许修改：{string.Join(", ", existNosInPerson)}");

        // 3) 解析主键
        var pairs = new List<(Guid Id, string NewNo)>();
        foreach (var x in normalized)
        {
            if (Guid.TryParse(x.Id, out var gid))
                pairs.Add((gid, x.NewNo));
        }

        if (pairs.Count == 0)
            return new Result<int>("ID格式不正确");

        var ids = pairs.Select(p => p.Id).ToList();

        // 4) 本表冲突校验
        var existNosInPool = await DC.Set<SysHrInterviewPool>()
            .AsNoTracking()
            .Where(e => newNos.Contains(e.HiredStaffNo) && !ids.Contains(e.ID))
            .Select(e => e.HiredStaffNo)
            .ToListAsync();
        if (existNosInPool.Count > 0)
            return new Result<int>($"以下工号已存在于录用候选池，不允许重复：{string.Join(", ", existNosInPool)}");

        // 5) 执行更新
        using var tx = await DC.Database.BeginTransactionAsync();
        try
        {
            var now = DateTime.Now;
            var user = Wtm.LoginUserInfo?.ITCode;
            var total = 0;

            foreach (var (gid, newNo) in pairs)
            {
                var affected = await DC.Set<SysHrInterviewPool>()
                    .Where(e => e.ID == gid)
                    .ExecuteUpdateAsync(s => s
                        .SetProperty(e => e.HiredStaffNo, newNo)
                        .SetProperty(e => e.UpdateTime, now)
                        .SetProperty(e => e.UpdateBy, user));
                total += affected;
            }

            await tx.CommitAsync();
            return new Result<int>(total);
        }
        catch (Exception ex)
        {
            await tx.RollbackAsync();
            return new Result<int>($"更新失败: {ex.Message}");
        }
    }


    public class UpdateHiredStaffNoInput
    {
        public string Id { get; set; }
        public string NewHiredStaffNo { get; set; }
    }
}